<?php

// $Id$

require_once(TANGRA_MAIN_DIR.'web_site/web_site_config.class.php');
require_once(TANGRA_MAIN_DIR.'db/db_connection.class.php');
require_once(TANGRA_MAIN_DIR.'web_site/web_context.class.php');
require_once(TANGRA_MAIN_DIR.'web_site/session_vars_manager.class.php');
require_once(TANGRA_MAIN_DIR.'nls/date.inc.php');
require_once(TANGRA_MAIN_DIR.'misc/inet_functions.inc.php');

require_once($WSC->get_site_inc_dir().'modules/tangra_cms/classes/vpage/tcms_vpage_dbc.class.php');
require_once($WSC->get_site_inc_dir().'modules/tangra_cms/classes/vpage/tcms_vpage_template_dbc.class.php');
require_once($WSC->get_site_inc_dir().'modules/tangra_cms/classes/settings/tcms_settings_cache_dbc.class.php');
require_once($WSC->get_site_inc_dir().'modules/tangra_cms/classes/settings/tcms_settings_dir_index_dbc.class.php');


class TCMS_Request_Handler extends Tangra_Class {
	private $config;
	private $dbc;
	private $context;
	private $svm;
	private $path;
	private $query;


	function __construct(Web_Site_Config $wsc, DB_Connection $dbc, Web_Context $context) {
		$this->config = $wsc;
		$this->dbc = $dbc;
		$this->context = $context;
		$this->svm = $this->get_svm();
		$parsed = parse_url($context->get_from_server('REQUEST_URI'));

		if ($parsed !== false && is_array($parsed)) {
			$this->path = urldecode($parsed['path']);
			while(strpos($this->path, '//') !== FALSE) {
				$this->path = str_replace('//', '/', $this->path);
			}
	
			if (array_key_exists('query', $parsed)) {
				$this->query = $parsed['query'];
				$this->context->set_var('GET', url_query_to_array($this->query));				
			} else {
				$this->query = '';
			}
		}
	}


	public function handle_request() {
		$mode = $this->detect_mode();

		switch ($mode) {
			case 0:
				$ret = $this->handle_404();
				break;
			case 1:
				$ret = $this->handle_direct_hit();
				break;
			default:
				$ret = $this->handle_404();
				break;
		}


		return $ret;
	}



	private function decompose_path($path) {
		$ret = false;

		if (trim($path)) {
			$path_tmp = substr(trim($path), 1, strlen($path) - 2);
			$ret = explode('/', $path_tmp);
		}

		return $ret;
	}


	private function log_not_found($path) {
		$log_dir = $this->config->get_logs_dir();
		$not_found_log = $log_dir.'not_found.log';
		error_log(date('Y-m-d H:i:s')." $path\r\n", 3, $not_found_log);
	}


	private function get_svm() {
		$svm_var_name = Session_Vars_Manager::get_svm_var_name();
		if ($this->context->exists_in_session($svm_var_name)) {
			$svm = $this->context->get_from_session($svm_var_name);

			if (!($svm instanceof Session_Vars_Manager)) {
				throw new TE_Session_Vars_Manager_Overwrited();
			}
		} else {
			$session = &$this->context->get_var('SESSION');
			$session[$svm_var_name] = new Session_Vars_Manager();
			$svm =  $session[$svm_var_name];
		}

		return $svm;
	}


	private function detect_mode() {

		$base = $this->compose_base();

		$direct_hit_url = $base.'modules/tangra_cms/tangra_cms.php';
		if (substr($this->path, 0, strlen($direct_hit_url)) == $direct_hit_url) {
			$ret = 1; // direct hit
		} else {
			$ret = 0; // via 404
		}

		return $ret;
	}


	private function compose_base() {
		$port =  $this->context->get_from_server('SERVER_PORT');

		if ($this->context->exists_in_server('HTTPS')) {
			$https = $this->context->get_from_server('HTTPS');
		} else {
			$https = false;
		}
		$protocol = ($https) ? 'https' : 'http';

		if ($port == '80') {
			$port = '';
		} else {
			$port = ':'.$port;
		}

		return $this->config->get_document_root_relative();
	}


	private function handle_direct_hit() {
		if ($this->context->exists_in_get('vpage') && $this->context->exists_in_get('previewcode')) {

			$vpage_id = $this->context->get_from_get('vpage');
			$preview_code = $this->context->get_from_get('previewcode');

			if ($this->svm->is_global_var_registered('preview_code')) {
				$preview_code_sess = $this->svm->get_global_var('preview_code');
				if ($preview_code_sess == $preview_code) {
					$vpage = new TCMS_VPage_DBC();
					if ($vpage->load_by_id($this->dbc, $vpage_id)) {
						$template = new TCMS_VPage_Template_DBC();
						if ($template->load_by_id($this->dbc, $vpage->get_template())) {
							require_once($this->config->get_site_inc_dir().$template->get_wp_path());
							$page_class = $template->get_wp_class_name();
							$page_object = new $page_class($vpage, $template);

							$this->show_page_direct($vpage, $page_object);
						} else {
							printbr('Cannot load template with id '.$vpage->get_template());
						}

					} else {
						printbr('Cannot load vpage with id '.$vpage_id);
					}
				}
			}
		}
	}


	private function handle_404() {
		global $SITE;

		$vpage = new TCMS_VPage_DBC();
		$path = substr($this->path, 1); // stripping leading slash

		if ($vpage->load_by_path($this->dbc, $path) && $vpage->get_online()) {
			// Nominal event - page path exists as is
			$this->process_page($vpage, $path);
		} else {
			$settings_dir_index = new TCMS_Settings_Dir_Index_DBC();
			$dbc = $this->dbc;
			$settings_dir_index->load_by_id($dbc, 1);
			
			$found = false;			
			if ($settings_dir_index->get_switch()) { // Do we have directory index files lookup swtiched on
				$index_arr = $settings_dir_index->get_files_arr();


				if ($index_arr) { // is there directory index files defined
					foreach($index_arr as $file) {
						if ($path) {
							$path_alt = tangra_normalize_path($path).$file;
						} else {
							$path_alt = $file;
						}
						if ($vpage->load_by_path($this->dbc, $path_alt) && $vpage->get_online()) {
							if ($path && substr($path, -1) != '/') {
								$path_alt = $path.'/';
								header("HTTP/1.1 301 Moved Permanently");
								header("Status: 301 Moved Permanently");
								$location = $this->get_absolute_location($path_alt);
								header("Location: ".$location);
								header("Connection: close");
								$found = true;
							} else {
								// we found page with that alternative path
								$this->process_page($vpage, $path_alt);
								$found = true;
								break;
							}
						}
					}

				}
			}

			// Patch for "crappy software that removes trailing forward slashes from your URLs."
			if (!$found) {
				if ($path && substr($path, -1) != '/') {
					$path_alt = $path.'/';
										
					if ($vpage->load_by_path($this->dbc, $path_alt)) {
						// we found page with that alternative path
						header("HTTP/1.1 301 Moved Permanently");
						header("Status: 301 Moved Permanently");
						$location = $this->get_absolute_location($path_alt);
						header("Location: ".$location);
						header("Connection: close");
						$found = true;
					}
				}
			}			
			
			if (!$found) {
				// page not found. Leaving 404 header					
				require_once($this->config->get_site_inc_dir().'modules/tangra_cms/pages/_regular/tangra_cms/tangra_cms_wp.class.php');
				$page = new Tangra_CMS_WP('not_found');
				$SITE->run($page, $this->context);
			}
			
		}
	}


	private function show_page_direct($vpage, $page) {
		global $SITE;

		$timestamp = datetime_to_timestamp($vpage->get_last_modified());
		$gmt_mtime = gmdate('D, d M Y H:i:s', $timestamp) . ' GMT';
		header("Last-Modified: " . $gmt_mtime);

		$SITE->run($page, $this->context);
	}


	private function show_page_direct_200($vpage, $page, $vpage_last_modified) {
		header('HTTP/1.0 200 OK');
		header('Status: 200 OK');
		$this->show_page_direct($vpage, $page, $vpage_last_modified);
	}


	private function show_page_regen_cache($vpage, $page, $cache_file, $vpage_last_modified, $expires = false) {
		ob_start();
		$this->show_page_direct_200($vpage, $page, $vpage_last_modified);
		$cache = ob_get_contents();
		file_put_contents($cache_file, $cache);
		@chmod($cache_file, 0664);
		if ($expires) {
			header('Expires: '.gmdate('D, d M Y H:i:s', datetime_to_timestamp($expires)).' GMT');
		}
		ob_end_flush();
	}


	private function get_absolute_location($path) {
		$server = $this->context->get_from_server('SERVER_NAME');
		$port =  $this->context->get_from_server('SERVER_PORT');
		if ($port == '80') {
			$port = '';
		} else {
			$port = ':'.$port;
		}

		if ($this->context->exists_in_server('HTTPS')) {
			$https = $this->context->get_from_server('HTTPS');
		} else {
			$https = false;
		}

		$protocol = ($https) ? 'https' : 'http';

		$params = $this->query;

		return $protocol.'://'.$server.$port.'/'.$path.($params ? '?'.$params : '');
	}


	private function process_page($vpage, $path) {
		$template = new TCMS_VPage_Template_DBC();
		if ($template->load_by_id($this->dbc, $vpage->get_template())) {
			$settings_cache = new TCMS_Settings_Cache_DBC();
			if (!$settings_cache->load_by_id($this->dbc, 1)) {
				throw new Tangra_Module_Exception('Cannot load cache settings.');
			}

			require_once($this->config->get_site_inc_dir().$template->get_wp_path());
			$page_class = $template->get_wp_class_name();
			$page = new $page_class($vpage, $template);
			$vpage_last_modified = $page->get_vpage_last_modified();

			$dbc = $this->dbc;
			// category pages may need update of last_modified
			if ($vpage_last_modified > $vpage->get_last_modified()) {
				try {
					$dbc->start_trans();
					TCMS_VPage_DBC::update_last_modified($dbc, $vpage->get_id(), false, $vpage_last_modified);
					$dbc->complete_trans();
				} catch (Exception $e) {
					$dbc->fail_trans();
					$dbc->complete_trans();
					throw $e;
				}
			}

			$caching_enabled = $settings_cache->get_switch();
			$cache_locked = $settings_cache->get_locked() || $vpage->get_cache_locked();
			if ($caching_enabled && $vpage->get_cache()) {
				$path = str_replace('/', '-', $path).'-vp'.$vpage->get_id();
				$cache_file = $this->config->get_scratch_dir().'tcms_cache/'.$path.'.html';

				if ($vpage->get_cache_expiry() < 0) {
					if (file_exists($cache_file)) {
						$timestamp = filemtime($cache_file);
						$last_mod_timestamp = datetime_to_timestamp($vpage_last_modified);
						if ($last_mod_timestamp > $timestamp) { // vpage modified since cache generated
							if ($cache_locked) {
								$this->show_cached($cache_file, $timestamp, time() + 31449600); // 364 days ~ never expires
							} else {
								$this->show_page_regen_cache($vpage, $page, $cache_file, false);
							}
						} else {
							$this->show_cached($cache_file, $timestamp, time() + 31449600); // 364 days ~ never expires
						}
					} else {
						$this->show_page_regen_cache($vpage, $page, $cache_file, false);
					}
				} else {
					if ($vpage->get_cache_expiry() == 0) {
						$expiry_in_seconds = $settings_cache->get_expiry();
					} else {
						$expiry_in_seconds = $vpage->get_cache_expiry();
					}
					if (file_exists($cache_file)) {
						$mtime = filemtime($cache_file);
						if ($mtime + $expiry_in_seconds < time()) {
							// cache is expired. Must regenerate
							if (!$cache_locked) {
								$this->show_page_regen_cache($vpage, $page, $cache_file, date('Y-m-d H:i:S', $mtime + $expiry_in_seconds));
							} else {
								$timestamp = datetime_to_timestamp($vpage_last_modified);
								$this->show_cached($cache_file, $timestamp, $expiry_in_seconds + $mtime);
							}
						} else {
							$timestamp = datetime_to_timestamp($vpage_last_modified);
							$file_mtime = filemtime($cache_file);
							if ($file_mtime < $timestamp) { // vpage modified since cache generated
								if ($cache_locked) {
									$this->show_cached($cache_file, $timestamp, $expiry_in_seconds + $mtime);
								} else {
									$this->show_page_regen_cache($vpage, $page, $cache_file, date('Y-m-d H:i:S', $mtime + $expiry_in_seconds));
								}
							} else {
								$this->show_cached($cache_file, $timestamp, $expiry_in_seconds + $mtime);
							}
						}
					} else {
						$this->show_page_regen_cache($vpage, $page, $cache_file, date('Y-m-d H:i:S', $expiry_in_seconds));
					}
				}
			} else {
				$this->show_page_direct_200($vpage, $page, $vpage_last_modified);
			}
		} else {
			throw new Tangra_Module_Exception('Cannot load template with ID = '.$vpage->get_template());
		}
	}


	private function show_cached($cache_file, $last_modified_ts, $expires_ts) {
		if (function_exists('getallheaders')) {
			$req_headers = getallheaders();
			if (array_key_exists('If-Modified-Since', $req_headers)) {
				$ims = strtotime($req_headers['If-Modified-Since']);
				if ($ims) {
					if ($last_modified_ts <= $ims) {
						header('HTTP/1.0 304 Not Modified');
						header('Status: 304 Not Modified');
					} else {
						$this->_show_cached($cache_file, $last_modified_ts, $expires_ts);
					}
				} else {
					//error on parsing date from If-Modified-Since header. Just sending cached content
					$this->_show_cached($cache_file, $last_modified_ts, $expires_ts);
				}
			} else {
				$this->_show_cached($cache_file, $last_modified_ts, $expires_ts);
			}
		} else {
			$this->_show_cached($cache_file, $last_modified_ts, $expires_ts);
		}
	}


	private function _show_cached($cache_file, $last_modified_ts, $expires_ts) {
		header('HTTP/1.0 200 OK');
		header('Status: 200 OK');
		header("Last-Modified: " . gmdate('D, d M Y H:i:s', $last_modified_ts) . ' GMT');
		header('Expires: '.gmdate('D, d M Y H:i:s', $expires_ts).' GMT');
		readfile($cache_file);
	}
}